home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2010 April
/
PCWorld0410.iso
/
pluginy Firefox
/
138
/
138.xpi
/
chrome
/
stumbleupon.jar
/
content
/
stumbleReporter.js
< prev
next >
Wrap
Text File
|
2009-08-17
|
13KB
|
440 lines
//
// su_StumbleReporter
//
// This class encapsulates all of the stumble reporting logic, including storage of stumbled urls and report
// submission retry logic.
//
function su_StumbleReporter(parent)
{
this._parent = parent;
this._ds = this._parent.getDatastore();
this._updateEnableDb();
this._timer = null;
this._reportContext = null;
this._lastReportTimestamp = 0;
this._retryCount = 0;
}
su_StumbleReporter.prototype =
{
FAIL_NO_SEENCONF: -1,
FAIL_NORMAL_TIMEOUT: -2,
FAIL_BACKUP_TIMEOUT: -3,
start: function()
{
if (this._timer)
return;
var reportInterval = this._ds.getValue("@stumble_report_interval_ms");
var me = this;
this._timer = this._parent._setInterval( function() { me._checkSendReport(); }, reportInterval );
this._lastReportTimestamp = this._ds.getIntValue("$stumble_last_report");
this._retryCount = this._ds.getValue("$stumble_report_retrycount");
},
stop: function()
{
if (! this._timer)
return;
if (this._reportContext)
this._parent.abortPostAsync(this._reportContext._request);
this._parent._clearInterval(this._timer);
this._timer = null;
this._lastReportTimestamp = 0;
this._retryCount = 0;
},
addVisitedUrl: function(url)
{
if (!url.publicid)
return;
// First, see if we have to remove one.
if (this._enabledb)
{
var db = this._ds.getDatabase();
// prevent the queue from growing without bound
db.a("select * from stumble_visited_urls order by stumbletime asc");
var result = this._failsafeQuery(db);
if (result.length >= this._ds.getValue("@stumble_max_visited_urls"))
this.deleteVisitedUrl(result[0]);
// Insert this visited url into the database.
db = this._ds.getDatabase();
db.a("INSERT INTO stumble_visited_urls (publicid, stumbletime, referralid, retrycount) VALUES (");
db.as(url.publicid);
db.av(this._parent.callWindow("su_get_time_s"));
var referralId = '';
if (url.referralid)
referralId = url.referralid;
db.as(referralId);
db.alv(this._retryCount);
this._failsafeQuery(db);
}
else
{
// prevent the queue from growing without bound
var result = this._ds.selectAllRows("stumble_visited_urls");
if (result.length >= this._ds.getValue("@stumble_max_visited_urls"))
{
result.sort(function (a, b)
{ return parseInt(a.stumbletime) > parseInt(b.stumbletime); });
this.deleteVisitedUrl(result[0]);
}
// Insert this visited url into the database.
var row = new Object();
row.publicid = url.publicid;
row.stumbletime = this._parent.callWindow("su_get_time_s");
row.referralid = (url.referralid) ? url.referralid : '';
row.retrycount = this._retryCount;
if (! this._ds.selectRow("stumble_visited_urls", "publicid", row.publicid, "referralid", row.referralid))
this._ds.insertRow("stumble_visited_urls", row);
this._ds.flushPrefs();
}
// And perform an active report submission.
this._reportStumbles(true);
},
deleteVisitedUrl: function(url)
{
if (this._enabledb)
{
var db = this._ds.getDatabase();
db.a("delete from stumble_visited_urls where publicid = " + db.q(url.publicid) + " and referralid=" + db.q(url.referralid));
this._failsafeQuery(db);
}
else
{
var row = this._ds.selectRow("stumble_visited_urls", "publicid", url.publicid, "referralid", url.referralid);
if (row)
this._ds.deleteRow(row);
var referralid = (url.referralid) ? url.referralid : '';
}
},
_isReportInProgress: function()
{
return (this._ds.getIntValue("$stumble_report_started") != 0)
},
_checkSendReport: function()
{
// First see if there is anything to report.
var url = this._getNextVisitedUrl();
if (url == null)
return;
// See if a report is already in progress.
if (this._isReportInProgress())
{
// Check whether it timed out.
var submissionTimeout = this._ds.getValue("@stumble_submission_timeout_ms");
var finishTime = submissionTimeout + this._ds.getIntValue("$stumble_report_started");
var now = (new Date()).getTime();
// Note: We check for timeout, but we also check _reportContext because it is reset when the browser restarts.
// So if by chance the user gets a stumble_report_started in the future, then we can still detect a backup timeout
// when their browser is restarted because _reportContext will be reset to NULL in that case.
if (!this._reportContext || (finishTime <= now))
{
// This report timed out, abort the request.
this._onReportDone({ aborted: true, failureCode: this.FAIL_BACKUP_TIMEOUT });
return;
}
else
{
// It didn't time out yet, nothing to do right now.
return;
}
}
// Check whether we are in RTO (Retransmission TimeOut) mode.
if (this._retryCount != 0)
{
// Get the timeout value for the current state.
var timeout = this._getCurrentRTOTimeout();
var now = (new Date()).getTime();
if (now > (this._lastReportTimestamp + timeout))
{
// We timed out, bump / store the state, and try reporting stumbles again.
this._retryCount++;
this._ds.setValue("$stumble_report_retrycount", this._retryCount);
this._ds.flushPrefs();
this._reportStumbles(false);
}
}
else
{
// Nobody is actively reporting stumbles, and we aren't in RTO mode, so just report it.
// This is the state that we will be in when we have just begun successfully reporting
// queued stumbles that had previously failed or been queued up behind a failure.
this._reportStumbles(false);
}
},
//
// _failsafeQuery
//
// We use this function for all stumble reporter database queries. If we encounter errors performing database
// queries, then we switch to using prefs for the data storage. We are doing this because we have seen error
// logs that indicate that a number of clients are not able to update the database.
//
_failsafeQuery: function(db)
{
try
{
return db.query();
}
catch(ex)
{
this._ds.setValue("@db_visitedurls_fail", true);
this._updateEnableDb();
this._parent.callWindow("su_log_error", "DB_VISITEDURLS_FAIL: " + ex);
throw ex;
}
},
_updateEnableDb: function()
{
this._enabledb = this._ds._enabledb;
if(this._enabledb &&
(this._ds.getValue("@enable_visitedurls_failsafe") && this._ds.getValue("@db_visitedurls_fail")))
{
// If failsafe is enabled and we have had a visitedurls failure, them fall back to the prefs
// store.
this._enabledb = false;
}
},
// Returns the current RTO timeout in milliseconds.
_getCurrentRTOTimeout: function()
{
var timeout = this._ds.lookup("stumblereportretrycount:timeout", this._retryCount);
if (timeout == null)
timeout = this._ds.lookup("stumblereportretrycount:timeout", "last");
// Note: Value is stored in the ds in seconds.
return timeout * 1000;
},
_getNextVisitedUrl: function()
{
if (this._enabledb)
{
var db = this._ds.getDatabase();
db.a("select * from stumble_visited_urls order by stumbletime asc");
var result = this._failsafeQuery(db);
if (result.length == 0)
return null;
return result[0];
}
else
{
var result = this._ds.selectAllRows("stumble_visited_urls");
if (! result)
return null;
if (result.length == 0)
return null;
result.sort(function (a, b)
{ return parseInt(a.stumbletime) > parseInt(b.stumbletime); });
return result[0];
}
},
_reportStumbles: function(active)
{
// Get the next URL to report.
var url = this._getNextVisitedUrl();
if (!url)
{
// nothing to do.
return;
}
// See if a report is already in progress.
if (this._isReportInProgress())
{
// Someone is still trying to submit, so we won't.
return;
}
var now = (new Date()).getTime();
if (!active)
{
var throttleTime = this._ds.getValue("@stumble_report_throttle_ms");
// If we are passively reporting stumbles, then we want to throttle the reporting so we only
// send at most 1 per minute. This avoids hammering the server right after it has returned from a server-down
// situation.
if (now < (this._lastReportTimestamp + throttleTime))
{
// Not enough time has elapsed, don't do it.
return;
}
}
// Update our state indicators so all toolbars know we are already submitting a report.
this._lastReportTimestamp = now;
this._reportContext = { url: url, active: active, quiet: true };
this._ds.setValue("$stumble_last_report", now);
this._ds.setValue("$stumble_report_started", now);
this._ds.flushPrefs();
// Now submit the report.
var lastFailure = this._ds.getValue("$stumble_report_lastfail");
var params = "";
params = this._arp(params, "stumble_pid", url.publicid);
params = this._arp(params, "stumble_time", url.stumbletime);
params = this._arp(params, "retry", url.retrycount);
params = this._arp(params, "lastfailure", lastFailure);
params = this._arp(params, "clienttime", this._parent.callWindow("su_get_time_s"));
params = this._arp(params, "houroffset", Math.floor((new Date()).getTimezoneOffset() / 60));
if (url.referralid && (url.referralId != ''))
params = this._arp(params, "stumble_rid", url.referralid);
var me = this;
this._parent.callWindow(
"su_post_url_server_async",
"report_stumble.php",
params,
15000,
function() { me._onReportDone.apply(me, arguments); } ,
this._reportContext );
},
_onReportDone: function(res)
{
var context = {};
if (res.detail)
context = res.detail;
// We are no longer in progress
this._reportContext = null;
this._ds.setValue("$stumble_report_started", 0);
this._ds.flushPrefs();
// Check for timeout or failure
if (res.aborted || (res.status != 200))
{
if(res.aborted)
{
if(!res.failureCode)
res.failureCode = this.FAIL_NORMAL_TIMEOUT;
}
else
{
res.failureCode = res.status;
}
this._onReportFailed(res);
return;
}
var strResponse = "";
if (typeof(res.responseText) != "undefined")
strResponse = res.responseText;
if (this._parent._logCommunicationEnabled)
su_log("response report_stumble.php", strResponse);
// Parse the response, on success it will have the SEENCONF command.
this._parent.callWindow(
"su_process_commands",
strResponse,
context);
if (context.confirmed)
{
// We got a confirmation, reset the retry count, remove the URL from
// the visited list, and report the next one.
this._retryCount = 0;
this._ds.setValue("$stumble_report_retrycount", this._retryCount);
this.deleteVisitedUrl(context.url);
this._ds.flushPrefs();
this._parent._setTimeout(
function (this_, active) {
this_._reportStumbles(active); },
0,
this,
context.active);
}
else
{
// If we didn't get SEENCONF, then this this is a failure..
this._onReportFailed( { failureCode:this.FAIL_NO_SEENCONF } );
}
},
_onReportFailed: function(result)
{
// This is a failure, move the retransmission state to 1 if it is the first
// failure.
if (this._retryCount == 0)
{
this._retryCount = 1;
this._ds.setValue("$stumble_report_retrycount", this._retryCount);
}
// Update any URLs currently in the database to reflect the new retry count
this._ds.setValue("$stumble_report_lastfail", result.failureCode);
this._updateVisitedUrlsRetryCount();
this._ds.flushPrefs();
},
_updateVisitedUrlsRetryCount: function()
{
if (this._enabledb)
{
var db = this._ds.getDatabase();
db.a("UPDATE stumble_visited_urls SET retrycount=" + db.v(this._retryCount));
this._failsafeQuery(db);
}
else
{
var result = this._ds.selectAllRows("stumble_visited_urls");
if (! result)
return;
var i;
for (i = 0; i < result.length; i++)
{
result[i].retrycount = this._retryCount;
this._ds.updateRow(result[i]);
}
}
},
_arp: function (paramStr, name, value, optGetFlag)
{
var delimiter;
if (optGetFlag && (paramStr.indexOf("&") == -1) &&
(paramStr.indexOf("?") == -1))
delimiter = "?";
else
delimiter = "&";
return paramStr + ((paramStr == "") ? "" : delimiter) + name + "=" +
encodeURIComponent(value);
}
}